/*!
* @file BoundBox3D.cpp
* @brief 三维包围盒
* @date 2024/9/12
*/
#include <algorithm>
#include "BoundBox3D.h"
#include "Matrix4D.h"

using namespace data;

template<class float_type>
BoundBox3<float_type>::BoundBox3(const vector_type* points, std::size_t cnt)
{
    MakeEmpty();
    const vector_type* pI = nullptr;
    const vector_type* pEnd = points + cnt;
    for (pI = points; pI < pEnd; ++pI) 
    {
        _minX = std::min(_minX, pI->x);
        _minY = std::min(_minY, pI->y);
        _minZ = std::min(_minZ, pI->z);
        _maxX = std::max(_maxX, pI->x);
        _maxY = std::max(_maxY, pI->y);
        _maxZ = std::max(_maxZ, pI->z);
    }
}

template<class float_type>
BoundBox3<float_type>::BoundBox3(const vector_type& point, float_type distance)
    : _minX(point.x - distance)
    , _minY(point.y - distance)
    , _minZ(point.z - distance)
    , _maxX(point.x + distance)
    , _maxY(point.y + distance)
    , _maxZ(point.z + distance)
{}

template<class float_type>
inline void BoundBox3<float_type>::MakeEmpty()
{
    _minX = _minY = _minZ = traits_type::max();
    _maxX = _maxY = _maxZ = -traits_type::max();
}

template<class float_type>
bool BoundBox3<float_type>::IsValid() const
{
    return ((_minX <= _maxX) && (_minY <= _maxY) && (_minZ <= _maxZ));
}

template<class float_type>
void BoundBox3<float_type>::Add(const vector_type& vec)
{
    _minX = std::min(_minX, vec.x);
    _minY = std::min(_minY, vec.y);
    _minZ = std::min(_minZ, vec.z);
    _maxX = std::max(_maxX, vec.x);
    _maxY = std::max(_maxY, vec.y);
    _maxZ = std::max(_maxZ, vec.z);
}

template<class float_type>
bool BoundBox3<float_type>::Contains(const vector_type& vec) const
{
    if (vec.x < _minX || vec.x > _maxX) {
        return false;
    }
    if (vec.y < _minY || vec.y > _maxY) {
        return false;
    }
    if (vec.z < _minZ || vec.z > _maxZ) {
        return false;
    }
    return true;
}

template<class float_type>
bool BoundBox3<float_type>::Intersect(const vector_type& point, const vector_type& dir) const
{
    float_type dist = (dir % (Center() - point)).Length() / dir.Length();
    if (dist > Diagonal())
        return false;

    vector_type base[6] = { 
        vector_type(_minX, _minY, _maxZ),vector_type(_maxX, _minY, _maxZ),vector_type(_minX, _maxY, _maxZ),
        vector_type(_minX, _minY, _maxZ),vector_type(_minX, _minY, _maxZ),vector_type(_minX, _minY, _minZ) };
    vector_type normal[6] = {
        vector_type(1.0f, 0.0f, 0.0f),vector_type(1.0f, 0.0f, 0.0f),vector_type(0.0f, 1.0f, 0.0f),
        vector_type(0.0f, 1.0f, 0.0f),vector_type(0.0f, 0.0f, 1.0f),vector_type(0.0f, 0.0f, 1.0f) };
    for (int i = 0; i < 6; i++)
    {
        float_type dot = normal[i] * dir;
        if (dot == 0.0f)
            continue;

        float_type k = (normal[i] * (base[i] - point)) / dot;
        if (Contains(point + dir * k))
            return true;
    }
    return false;
}

template<class float_type>
void BoundBox3<float_type>::Add(const BoundBox3<float_type>& bbox)
{
    _minX = std::min(_minX, bbox._minX);
    _maxX = std::max(_maxX, bbox._maxX);
    _minY = std::min(_minY, bbox._minY);
    _maxY = std::max(_maxY, bbox._maxY);
    _minZ = std::min(_minZ, bbox._minZ);
    _maxZ = std::max(_maxZ, bbox._maxZ);
}

template<class float_type>
bool BoundBox3<float_type>::Contains(const BoundBox3<float_type>& bbox) const
{
    if (bbox._minX < _minX || bbox._maxX > _maxX) {
        return false;
    }
    if (bbox._minY < _minY || bbox._maxY > _maxY) {
        return false;
    }
    if (bbox._minZ < _minZ || bbox._maxZ > _maxZ) {
        return false;
    }
    return true;
}

template<class float_type>
bool BoundBox3<float_type>::Intersect(const BoundBox3<float_type>& bbox) const
{
    if (bbox._maxX < _minX || bbox._minX > _maxX) {
        return false;
    }
    if (bbox._maxY < _minY || bbox._minY > _maxY) {
        return false;
    }
    if (bbox._maxZ < _minZ || bbox._minZ > _maxZ) {
        return false;
    }
    return true;
}

template<class float_type>
BoundBox3<float_type> BoundBox3<float_type>::Intersected(const BoundBox3<float_type>& bbox) const
{
    BoundBox3<float_type> cBBRes;
    cBBRes._minX = std::max(_minX, bbox._minX);
    cBBRes._maxX = std::min(_maxX, bbox._maxX);
    cBBRes._minY = std::max(_minY, bbox._minY);
    cBBRes._maxY = std::min(_maxY, bbox._maxY);
    cBBRes._minZ = std::max(_minZ, bbox._minZ);
    cBBRes._maxZ = std::min(_maxZ, bbox._maxZ);
    return cBBRes;
}

template<class float_type>
BoundBox3<float_type> BoundBox3<float_type>::United(const BoundBox3<float_type>& bbox) const
{
    BoundBox3<float_type> cBBRes;
    cBBRes._minX = std::min(_minX, bbox._minX);
    cBBRes._maxX = std::max(_maxX, bbox._maxX);
    cBBRes._minY = std::min(_minY, bbox._minY);
    cBBRes._maxY = std::max(_maxY, bbox._maxY);
    cBBRes._minZ = std::min(_minZ, bbox._minZ);
    cBBRes._maxZ = std::max(_maxZ, bbox._maxZ);
    return cBBRes;
}

template<class float_type>
bool BoundBox3<float_type>::IsEqual(const BoundBox3<float_type>& bbox, float_type tol) const
{
    return Minimum().IsEqual(bbox.Minimum(), tol)
        && Maximum().IsEqual(bbox.Maximum(), tol);
}

template<class float_type>
void BoundBox3<float_type>::Enlarge(float_type len)
{
    _minX -= len;
    _minY -= len;
    _minZ -= len;
    _maxX += len;
    _maxY += len;
    _maxZ += len;
}

template<class float_type>
void BoundBox3<float_type>::Shrink(float_type len)
{
    _minX += len;
    _minY += len;
    _minZ += len;
    _maxX -= len;
    _maxY -= len;
    _maxZ -= len;
}

template<class float_type>
void BoundBox3<float_type>::Move(const vector_type& vec)
{
    _minX += vec.x;
    _maxX += vec.x;
    _minY += vec.y;
    _maxY += vec.y;
    _minZ += vec.z;
    _maxZ += vec.z;
}

template<class float_type>
void BoundBox3<float_type>::Scale(const vector_type& scales)
{
    _minX *= scales.x;
    _maxX *= scales.x;
    _minY *= scales.y;
    _maxY *= scales.y;
    _minZ *= scales.z;
    _maxZ *= scales.z;
}

template<class float_type>
void BoundBox3<float_type>::Transform(const Matrix4d& mat)
{
    (*this) = Transformed(mat);
}

template<class float_type>
BoundBox3<float_type> BoundBox3<float_type>::Transformed(const Matrix4d& mat) const
{
    vector_type corners[8];
    GetCorners(corners);
    BoundBox3<float_type> bbox;
    for (int i = 0; i < 8; i++) 
    {
        bbox.Add(mat.MultVec(corners[i]));
    }
    return bbox;
}

template<class float_type>
Vector3<float_type> BoundBox3<float_type>::Minimum() const
{
    return vector_type(_minX, _minY, _minZ);
}

template<class float_type>
Vector3<float_type> BoundBox3<float_type>::Maximum() const
{
    return vector_type(_maxX, _maxY, _maxZ);
}

template<class float_type>
Vector3<float_type> BoundBox3<float_type>::Center() const
{
    return vector_type((_maxX + _minX) / 2, (_maxY + _minY) / 2, (_maxZ + _minZ) / 2);
}

template<class float_type>
void BoundBox3<float_type>::GetCorners(vector_type corners[8]) const
{
    corners[0].Set(_minX, _minY, _minZ);
    corners[1].Set(_maxX, _minY, _minZ);
    corners[2].Set(_minX, _maxY, _minZ);
    corners[3].Set(_maxX, _maxY, _minZ);
    corners[4].Set(_minX, _minY, _maxZ);
    corners[5].Set(_maxX, _minY, _maxZ);
    corners[6].Set(_minX, _maxY, _maxZ);
    corners[7].Set(_maxX, _maxY, _maxZ);
}

template<class float_type>
float_type BoundBox3<float_type>::LengthX() const
{
    return _maxX - _minX;
}

template<class float_type>
float_type BoundBox3<float_type>::LengthY() const
{
    return _maxY - _minY;
}

template<class float_type>
float_type BoundBox3<float_type>::LengthZ() const
{
    return _maxZ - _minZ;
}

template<class float_type>
float_type BoundBox3<float_type>::Diagonal() const
{
    return sqrt(((_maxX - _minX) * (_maxX - _minX))
        + ((_maxY - _minY) * (_maxY - _minY))
        + ((_maxZ - _minZ) * (_maxZ - _minZ)));
}

template<class float_type>
float_type BoundBox3<float_type>::Volume() const
{
    if (!IsValid())
    {
        return -1.0;
    }
    return LengthX() * LengthY() * LengthZ();
}

//导出
namespace Base
{
    template class DATA_API BoundBox3<float>;
    template class DATA_API BoundBox3<double>;
}